package com.ctsi.commons.util.json;

import com.ctsi.commons.util.UtilDateTime;
import com.ctsi.commons.util.UtilMisc;
import com.ctsi.commons.util.UtilResource;
import com.ctsi.commons.util.UtilValidate;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 处理对象与json的转化，主要为了转化的个性化 toJson方法在表单中使用,注意,需要freemarker是按照参数个数来调用的,增加同名函数,主要参数个数要变化,否则要改名
 * 
 * @author zhangjw
 * 
 */
public abstract class JsonUtil {

	public static final Gson defaultGson = getGsonBuilder().create();

	private static GsonBuilder getGsonBuilder() {
		GsonBuilder result = regDefTypeAda(new GsonBuilder());
		Properties p = readConfig();
		String df = p.getProperty("DateFormat");
		int dfType = DateFormat.LONG;
		if (UtilValidate.isNotEmpty(df)) {
			dfType = Integer.valueOf(df);
		}
		result.setDateFormat(dfType);
		if (UtilMisc.checkBoolean(p.getProperty("format"))) {
			result.setPrettyPrinting();
		}
		if (UtilMisc.checkBoolean(p.getProperty("serializeNulls"), true)) {
			result.serializeNulls();
		}
		if (UtilValidate.isNotEmpty(p.getProperty("FieldNamingPolicy"))) {
			result.setFieldNamingPolicy(FieldNamingPolicy.valueOf(p.getProperty("FieldNamingPolicy")));
		}
		if (UtilMisc.checkBoolean(p.getProperty("NaNInfinity"), false)) {
			result.serializeSpecialFloatingPointValues();
		}		
		
		return result;
	}

	private static Properties readConfig() {
		String posion = "json.properties";

		return UtilResource.getProperties(posion);
	}

	private static final GsonBuilder regDefTypeAda(GsonBuilder gb) {
		gb.registerTypeAdapter(Date.class, new UtilDateSerializer());

		gb.registerTypeAdapter(Calendar.class, new UtilCalendarSerializer());

		gb.registerTypeAdapter(GregorianCalendar.class, new UtilCalendarSerializer());
		return gb;
	}

	public static String jsonEncoding(String souce) {
		return souce.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}

	/**
	 * 对jsonlib处理的字符串编码,如果json写在js程序中 jsonlib 把" 换为 \"
	 * 
	 * @param souce
	 * @return
	 */
	public static String jsonLibEncoding(String souce) {

		return strToJson(souce, false).replaceAll("\\\"", "\\\\\\\"").replaceAll("'", "\\'");
		// .replace("<", "&lt;").replace(">", "&gt;")
		// return souce;
	}

	/**
	 * 处理字段串为，可作为json内容的串
	 * 
	 * @param souce
	 * @return
	 */
	public static final String strToJson(String souce, boolean more) {

		if (more) {
			return souce.replaceAll("'", "\\'").replace("\"", "\\\"").replaceAll("\r\n", "\\n").replaceAll("\n", "\\n");
		} else {
			return souce.replaceAll("\r\n", "\\n").replaceAll("\n", "\\n");
		}

		// .replace("<", "&lt;").replace(">", "&gt;")
		// return souce;
	}

	private static final void removeKey(String[] excludes, JsonObject jo) {
		if (UtilValidate.isEmpty(excludes)) {
			return;
		}
		for (String e : excludes) {
			jo.remove(e);
		}

	}

	private static final void removeObj(JsonElement je, String[] excludes) {
		if (je instanceof JsonObject) {
			JsonObject jo = (JsonObject) je;
			removeKey(excludes, jo);
		} else if (je instanceof JsonArray) {
			for (JsonElement jeInar : (JsonArray) je) {
				removeObj(jeInar, excludes);
			}
		}
	}

	private static final String removeNoNeed(JsonElement je, String[] excludes, Gson gso) {
		removeObj(je, excludes);
		return gso.toJson(je);
	}

	private static final void removeNoNeed(JsonElement je, String[] excludes, Gson gso, Appendable writer) {
		removeObj(je, excludes);
		gso.toJson(je, writer);
	}
	private static final String toJsonIntenal(boolean check, String dataForm,Object bean, String[] excludes,boolean isSerializeNull) {
		
		final Gson gso=createGson(dataForm, check, isSerializeNull);
		JsonElement je = gso.toJsonTree(bean);
		return removeNoNeed(je, excludes, gso);
	}
	public static final String toJson(Object obj, String[] excludes) {
		JsonElement je = defaultGson.toJsonTree(obj);
		return removeNoNeed(je, excludes, defaultGson);
	}

	public static final void toJson(Object obj, String[] excludes, Appendable writer) {
		JsonElement je = defaultGson.toJsonTree(obj);
		removeNoNeed(je, excludes, defaultGson, writer);
	}

	public static final String toJson(Object bean) {
		return defaultGson.toJson(bean);
	}

	public static final void toJsonAppend(Object bean, Appendable writer) {
		defaultGson.toJson(bean, writer);
	}

	public static final Gson createGson(String datePattern, boolean check, boolean isSerializeNull) {
		GsonBuilder gb = new GsonBuilder();

		if (UtilValidate.isNotEmpty(datePattern)) {
			gb.setDateFormat(datePattern);
			gb.registerTypeAdapter(Date.class, new DateFormatTypeAdapter(datePattern));
		} else {
			regDefTypeAda(gb);
		}

		if (check)
			gb.excludeFieldsWithoutExposeAnnotation();
		if (isSerializeNull) {
			gb.serializeNulls();
		}
		return gb.create();
	}

	public static final String toJson(Object bean, String[] excludes, String datePattern) {
		return toJson(bean, excludes, false, datePattern, false);
	}

	public static final String toJson(Object bean, boolean checkAnn, String datePattern, boolean isSerializeNull) {
		return toJson(bean, null, checkAnn, datePattern, isSerializeNull);
	}

	public static final String toJson(Object bean, String[] excludes, boolean checkAnn, String datePattern, boolean isSerializeNull) {
		if (checkAnn || UtilValidate.isNotEmpty(datePattern)) {
			Gson tem = createGson(datePattern, checkAnn, isSerializeNull);
			JsonElement je = tem.toJsonTree(bean);

			return removeNoNeed(je, excludes, tem);

		}
		return toJsonIntenal(checkAnn,datePattern,bean, excludes,isSerializeNull);
	}

	public static final String toJsonWithAnn(Object bean, boolean checkAnn) {
		if (checkAnn) {
			return createGson(null, checkAnn, false).toJson(bean);
		}
		return toJson(bean);
	}

	public static Map<String, Object> jsonToMap(String json) {
		return jsonToMap(json, null);
	}

	public static Map<String, Object> jsonToMap(String json, String datePattern) {
		Type mapType = new TypeToken<Map<String, Object>>() {}.getType();

		if (UtilValidate.isNotEmpty(datePattern)) {
			return createGson(datePattern, false, false).fromJson(json, mapType);
		}
		return defaultGson.fromJson(json, mapType);
	}
	public static <T> Map<String, T> jsonToMap(String json, String datePattern,TypeToken<Map<String, T>> mapType) {
		 

		if (UtilValidate.isNotEmpty(datePattern)) {
			return createGson(datePattern, false, false).fromJson(json, mapType.getType());
		}
		return defaultGson.fromJson(json, mapType.getType());
	}
	public static final <T> T json2Bean(String json, Class<T> clazz) {
		return defaultGson.fromJson(json, clazz);
	}

	public static final <T> T json2Bean(String json, Class<T> clazz, String datePattern) {
		if (UtilValidate.isNotEmpty(datePattern)) {
			return createGson(datePattern, false, false).fromJson(json, clazz);
		}
		return defaultGson.fromJson(json, clazz);
	}

	public static final <T> T json2Bean(JsonElement json, Class<T> clazz, String datePattern) {
		if (UtilValidate.isNotEmpty(datePattern)) {
			return createGson(datePattern, false, false).fromJson(json, clazz);
		}
		return defaultGson.fromJson(json, clazz);
	}

	public static final <T> T json2bean(String json, Type type) {
		return defaultGson.fromJson(json, type);
	}

	public static final List<Map<String, Object>> jsonArrayToList(String json) {
		Type listType = new TypeToken<List<Map<String, Object>>>() {
		}.getType();
		return defaultGson.fromJson(json, listType);
	}

	private static final JsonParser jsonParser = new JsonParser();

	public static final <T> List<T> json2List(String json, Class<T> clazz, String[] excludes, String datePattern) {
		Gson tem;
		if (UtilValidate.isNotEmpty(datePattern)) {
			tem = createGson(datePattern, false, false);
		} else {
			tem = defaultGson;
		}

		JsonArray result = jsonParser.parse(json).getAsJsonArray();
		if (result.size() == 0) {
			return Collections.emptyList();
		}
		List<T> list = new ArrayList<T>(result.size());
		if (UtilValidate.isNotEmpty(excludes)) {
			for (JsonElement je : result) {
				if (je instanceof JsonObject) {
					JsonObject jo = (JsonObject) je;
					removeKey(excludes, jo);

				}
				list.add(tem.fromJson(je, clazz));
			}
		} else {
			for (JsonElement je : result) {
				list.add(tem.fromJson(je, clazz));
			}
		}

		return list;
	}

	public static final List<?> json2List(String json, Type[] tp, String datePattern) {
		Gson tem;
		if (UtilValidate.isNotEmpty(datePattern)) {
			tem = createGson(datePattern, false, false);
		} else {
			tem = defaultGson;
		}

		JsonArray result = jsonParser.parse(json).getAsJsonArray();
		int jsize = result.size();
		if (jsize == 0) {
			return Collections.emptyList();
		}
		if (jsize != tp.length) {
			throw new RuntimeException("类型与数据数量不匹配");
		}
		List<Object> list = new ArrayList<Object>(jsize);

		for (int i = 0; i < jsize; i++) {
			JsonElement je = result.get(i);
			list.add(tem.fromJson(je, tp[i]));
		}

		return list;
	}

	public static final <T> List<T> json2List(String json, Class<T>[] clazz, String[] excludes, String datePattern) {
		Gson tem;
		if (UtilValidate.isNotEmpty(datePattern)) {
			tem = createGson(datePattern, false, false);
		} else {
			tem = defaultGson;
		}

		JsonArray result = jsonParser.parse(json).getAsJsonArray();
		int jsize = result.size();
		if (jsize == 0) {
			return Collections.emptyList();
		}

		if (jsize != clazz.length) {
			throw new RuntimeException("类型与数据数量不匹配");
		}
		List<T> list = new ArrayList<T>(jsize);
		if (UtilValidate.isNotEmpty(excludes)) {
			for (int i = 0; i < jsize; i++) {
				JsonElement je = result.get(i);
				if (je instanceof JsonObject) {
					JsonObject jo = (JsonObject) je;
					removeKey(excludes, jo);

				}
				list.add(tem.fromJson(je, clazz[i]));
			}
		} else {
			for (int i = 0; i < jsize; i++) {
				JsonElement je = result.get(i);
				list.add(tem.fromJson(je, clazz[i]));
			}
		}

		return list;
	}

	public static final <T extends Object> List<T> json2List(String data, Class<T> clazz) {
		return json2List(data, clazz, null, UtilDateTime.datePartition);

	}

	public static final JsonElement fromJson(String json) {
		return jsonParser.parse(json);
	}

	private static class UtilDateSerializer implements JsonSerializer<Date>, JsonDeserializer<Date> {

		public JsonElement serialize(Date date, Type type, JsonSerializationContext context) {
			return new JsonPrimitive(date.getTime());
		}

		public Date deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
			return new Date(element.getAsJsonPrimitive().getAsLong());
		}

	}

	private static class UtilCalendarSerializer implements JsonSerializer<Calendar>, JsonDeserializer<Calendar> {

		public JsonElement serialize(Calendar cal, Type type, JsonSerializationContext context) {
			return new JsonPrimitive(Long.valueOf(cal.getTimeInMillis()));
		}

		public Calendar deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
			Calendar cal = Calendar.getInstance();
			cal.setTimeInMillis(element.getAsJsonPrimitive().getAsLong());
			return cal;
		}

	}

	public static class DateFormatTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {

		private final DateFormat format;// 不能多线程访问

		public DateFormatTypeAdapter(String datePattern) {
			if (UtilValidate.isNotEmpty(datePattern)) {
				format = new SimpleDateFormat(datePattern);
			} else {
				format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			}

		}

		public JsonElement serialize(Date ts, Type t, JsonSerializationContext jsc) {
			String dfString = format.format(ts);
			return new JsonPrimitive(dfString);
		}

		public Date deserialize(JsonElement json, Type t, JsonDeserializationContext jsc) throws JsonParseException {
			if (!(json instanceof JsonPrimitive)) {
				throw new JsonParseException("The date should be a string value");
			}

			try {
				Date date = format.parse(json.getAsString());
				return date;
			} catch (ParseException e) {
				throw new JsonParseException(e);
			}
		}
	}

	/**
	 * 直接构造,也可以通过类信息得到
	 * 
	 * @param raw
	 *            外部类型
	 * @param args
	 *            泛型,参数化类型
	 * @return
	 */
	public static ParameterizedType type(final Class<?> raw, final Type... args) {
		return new ParameterizedType() {
			public Type getRawType() {
				return raw;
			}

			public Type[] getActualTypeArguments() {
				return args;
			}

			public Type getOwnerType() {
				return null;
			}
		};
	}
}
